package com.lp.biz.system.service.impl;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.Month;
import java.util.*;

import cn.hutool.json.JSONUtil;
import com.aliyun.green20220302.Client;
import com.aliyun.green20220302.models.TextModerationPlusRequest;
import com.aliyun.green20220302.models.TextModerationPlusResponse;
import com.aliyun.green20220302.models.TextModerationPlusResponseBody;
import com.aliyun.teautil.models.RuntimeOptions;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.lp.biz.member.mapper.MemberMapper;
import com.lp.biz.member.model.entity.Member;
import com.lp.biz.order.mapper.BizOrderMapper;
import com.lp.biz.order.model.entity.BizOrder;
import com.lp.biz.system.mapper.GreedScansMapper;
import com.lp.biz.system.mapper.SysMenuMapper;
import com.lp.biz.system.mapper.SystemMapper;
import com.lp.biz.system.model.dto.MenuDto;
import com.lp.biz.system.model.entity.GreedScans;
import com.lp.biz.system.model.vo.Home;
import com.lp.biz.system.model.vo.HomeChart;
import com.lp.biz.system.model.vo.MenuTree;
import com.lp.biz.system.model.vo.UpdatePassword;
import com.lp.biz.system.service.SysService;
import com.lp.common.component.AliService;
import com.lp.common.component.RedisService;
import com.lp.common.constant.GreenEnum;
import com.lp.common.context.ContextHolder;
import com.lp.common.context.UserDetail;
import com.lp.common.utils.CommonResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import static com.lp.common.constant.CacheConstant.*;
import static com.lp.common.constant.CategoryConstant.CODE_TRAINING;
import static com.lp.common.constant.HomeConstant.INTERVAL_TYPE_MONTH;
import static com.lp.common.constant.HomeConstant.INTERVAL_TYPE_YEAR;
import static com.lp.common.constant.OrderConstant.*;
import static com.lp.common.constant.RoleConstant.ADMIN;
import static com.lp.common.utils.HomeUtil.MONTH_STRING_MAP;

/**
 * @program: backend
 * @description: SysEndpoints 服务实现类
 * @author Ke.Song
 * @since 2024-05-19 00:37:24
 */
@Slf4j
@Service
public class SysServiceImpl implements SysService {

    @Value("${ali.green.confidence}")
    private Float confidence;

    @Autowired
    private MemberMapper memberMapper;

    @Autowired
    private RedisService redis;

    @Autowired
    private Client greenClient;

    @Autowired
    private SysMenuMapper menuMapper;

    @Autowired
    private AliService aliService;

    @Autowired
    private SystemMapper systemMapper;

    @Autowired
    private BizOrderMapper orderMapper;

    @Autowired
    private GreedScansMapper greedScansMapper;

    @Override
    public CommonResult getAllMenu() {
        UserDetail userDetail = ContextHolder.getContext().getUserDetail();
        Map<Integer, MenuTree> tempMaps = new LinkedHashMap<>(6);
        List<MenuDto> menuList = menuMapper.getAllMenu(userDetail.getRoles().contains(ADMIN) ? null : userDetail.getId());
        for (MenuDto dto : menuList) {
            MenuTree orDefault = tempMaps.getOrDefault(dto.getParentId(), new MenuTree());
            orDefault.setId(dto.getParentId());
            orDefault.setName(dto.getParentName());
            orDefault.setCode(dto.getParentCode());
            MenuTree children = new MenuTree();
            children.setId(dto.getId());
            children.setName(dto.getName());
            children.setCode(dto.getCode());
            orDefault.getChildren().add(children);
            tempMaps.put(dto.getParentId(), orDefault);
        }
        return CommonResult.success(tempMaps.values());
    }

    @Override
    public CommonResult getStsToken() {

        return CommonResult.success(aliService.getAcsResponse());
    }

    @Override
    public void logout(HttpServletRequest request) {
        Integer memberId = ContextHolder.getContext().getMemberId();
        HttpSession session = request.getSession(false);
        if (session != null) {
            session.invalidate();
        }
        redis.delete(USER_DETAIL_KEY + memberId);
        redis.delete(TOKEN_KEY + memberId);
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public boolean scanContent(String content, Integer memberId) {
        log.info("内容安全校验：开始校验，校验人 -> {}, 校验内容 -> {}", memberId, content);
        List<GreedScans> resultList = new ArrayList<>();
        boolean result = true;
        Map<String, Object> s = new HashMap<>(1);

        RuntimeOptions runtime = new RuntimeOptions();
        runtime.readTimeout = 10000;
        runtime.connectTimeout = 10000;
        s.put("content", content);
        for (GreenEnum value : GreenEnum.values()) {
            TextModerationPlusRequest textModerationRequest = new TextModerationPlusRequest();
            textModerationRequest.setService(value.getService());
            textModerationRequest.setServiceParameters(JSONUtil.toJsonStr(s));
            log.info("内容安全校验：校验中，校验人 -> {}, 校验内容 -> {}， 校验类型-> {}", memberId, content,
                    value.getService());
            try {
                TextModerationPlusResponse response = greenClient.textModerationPlusWithOptions(textModerationRequest, runtime);
                if (response.getStatusCode().equals(HttpStatus.OK.value())) {
                    TextModerationPlusResponseBody body = response.getBody();
                    if (body != null && body.getCode().equals(HttpStatus.OK.value())) {
                        String requestId = body.getRequestId();
                        if (body.getData() != null && body.getData().getResult() != null) {
                            for (TextModerationPlusResponseBody.TextModerationPlusResponseBodyDataResult o : body.getData().getResult()) {
                                if (o.getConfidence() != null && confidence.compareTo(o.getConfidence()) <= 0) {
                                    GreedScans greedScans = new GreedScans();
                                    greedScans.setMemberId(memberId);
                                    greedScans.setService(value.getService());
                                    greedScans.setContent(content);
                                    greedScans.setRequestId(requestId);
                                    greedScans.setConfidence(BigDecimal.valueOf(o.getConfidence()));
                                    greedScans.setLabel(o.getLabel());
                                    greedScans.setRiskWords(o.getRiskWords());
                                    resultList.add(greedScans);
                                }
                            }
                        }
                    }
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        if (!resultList.isEmpty()) {
            result = false;
            greedScansMapper.insertBatch(resultList);
        }
        log.info("内容安全校验：校验结束，校验人 -> {}, 校验内容 -> {}", memberId, content);
        return !result;
    }

    @Override
    public CommonResult getHome() {
        String s = redis.get(HOME_KEY);
        Home home;
        if (s == null) {
            home = memberMapper.getHome();
            if (home != null) {
                redis.put(HOME_KEY, JSONUtil.toJsonStr(home), 600L);
            }
        } else {
            home = JSONUtil.toBean(s, Home.class);
        }
        return CommonResult.success(home);
    }

    @Override
    public CommonResult getHomeChart(Integer intervalType) {
        Map<String, HomeChart> readingMap = new HashMap<>(6);
        Map<String, HomeChart> membersMap = new HashMap<>(6);
        if (intervalType.equals(INTERVAL_TYPE_YEAR)) {
            homoChartYear(readingMap, membersMap);
        } else if (intervalType.equals(INTERVAL_TYPE_MONTH)) {
            homoChartMonth(readingMap, membersMap);
        } else {
            homoChartDay(readingMap, membersMap);
        }
        return CommonResult.success(resultHandler(intervalType, readingMap, membersMap));
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void updatePassword(UpdatePassword password) {
        Integer memberId = ContextHolder.getContext().getMemberId();
        memberMapper.update(null, new UpdateWrapper<Member>().lambda().eq(Member::getId, memberId)
                .set(Member::getPassword, password.getPassword().trim()));
    }

    @Override
    public CommonResult getSysNotice() {
        return CommonResult.success(systemMapper.getSysNotice());
    }

    private void homoChartYear(Map<String, HomeChart> readingMap, Map<String, HomeChart> membersMap) {
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime start = LocalDateTime.of(now.getYear(), 1, 1, 0, 0, 0);
        for (BizOrder order : orderMapper.selectList(new QueryWrapper<BizOrder>().lambda()
                .eq(BizOrder::getStatus, STATUS_PAID).between(BizOrder::getCreateAt, start, now))) {
            String month = order.getCreateAt().getMonth().name();
            selectIntervalData(readingMap, membersMap, order, month);
        }
    }

    private void homoChartMonth(Map<String, HomeChart> readingMap, Map<String, HomeChart> membersMap) {
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime start = LocalDateTime.of(now.getYear(), now.getMonth(), 1, 0, 0, 0);
        for (BizOrder order : orderMapper.selectList(new QueryWrapper<BizOrder>().lambda()
                .eq(BizOrder::getStatus, STATUS_PAID).between(BizOrder::getCreateAt, start, now))) {
            String day = String.valueOf(order.getCreateAt().getDayOfMonth());
            selectIntervalData(readingMap, membersMap, order, day);
        }
    }

    private void homoChartDay(Map<String, HomeChart> readingMap, Map<String, HomeChart> membersMap) {
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime start = LocalDateTime.of(now.getYear(), now.getMonth(), now.getHour(), 0, 0, 0);
        for (BizOrder order : orderMapper.selectList(new QueryWrapper<BizOrder>().lambda()
                .eq(BizOrder::getStatus, STATUS_PAID).between(BizOrder::getCreateAt, start, now))) {
            String hour = String.valueOf(order.getCreateAt().getHour());
            selectIntervalData(readingMap, membersMap, order, hour);
        }
    }

    private void selectIntervalData(Map<String, HomeChart> readingMap, Map<String, HomeChart> membersMap, BizOrder order, String interval) {
        if (order.getOrderType().equals(ORDER_TYPE_MEMBERS)) {
            HomeChart orDefault = membersMap.getOrDefault(interval, new HomeChart());
            orDefault.setXAxis(interval);
            orDefault.setValue(orDefault.getValue() == null ? 1 : orDefault.getValue() + 1);
            membersMap.put(interval, orDefault);
        } else if (order.getOrderType().equals(ORDER_TYPE_TRAINING)) {
            HomeChart orDefault = readingMap.getOrDefault(interval, new HomeChart());
            orDefault.setXAxis(interval);
            orDefault.setValue(orDefault.getValue() == null ? 1 : orDefault.getValue() + 1);
            readingMap.put(interval, orDefault);
        }
    }

    /**
     * 根据间隔类型获取至当前的所有该类型的跨度
     * @param intervalType 间隔类型
     * @return 处理结果
     */
    private List<String> getFullInterval(Integer intervalType) {
        LocalDateTime now = LocalDateTime.now();
        List<String> interval = new ArrayList<>();
        if (intervalType.equals(INTERVAL_TYPE_YEAR)) {
            LocalDateTime start = LocalDateTime.of(now.getYear(), 1, 1, 0, 0, 0);
            while (start.isBefore(now)) {
                interval.add(start.getMonth().name());
                start = start.plusMonths(1);
            }
        } else if (intervalType.equals(INTERVAL_TYPE_MONTH)) {
            LocalDateTime start = LocalDateTime.of(now.getYear(), now.getMonth(), 1, 0, 0, 0);
            while (start.isBefore(now)) {
                interval.add(String.valueOf(start.getDayOfMonth()));
                start = start.plusDays(1);
            }
        } else {
            LocalDateTime start = LocalDateTime.of(now.getYear(), now.getMonth(), now.getDayOfMonth(), 0, 0, 0);
            while (start.isBefore(now)) {
                interval.add(String.valueOf(start.getHour()));
                start = start.plusHours(1);
            }
        }
        return interval;
    }

    private Map<String, List<HomeChart>> resultHandler(Integer intervalType, Map<String, HomeChart> readingMap, Map<String, HomeChart> membersMap) {
        List<HomeChart> readingList = new ArrayList<>(6);
        List<HomeChart> membersList = new ArrayList<>(6);
        for (String interval : getFullInterval(intervalType)) {
            HomeChart reading = readingMap.get(interval);
            if (reading == null) {
                reading = new HomeChart();
                reading.setValue(0);
                reading.setXAxis(interval);
            };
            readingList.add(reading);
            HomeChart members = membersMap.get(interval);
            if (members == null) {
                members = new HomeChart();
                members.setValue(0);
                members.setXAxis(interval);
            };
            membersList.add(members);
        }
        Map<String, List<HomeChart>> result = new HashMap<>(2);
        result.put(CODE_TRAINING, readingList);
        result.put("Members", membersList);

        if (intervalType.equals(INTERVAL_TYPE_YEAR)) {
            xAxisYearHandler(result);
        } else if (intervalType.equals(INTERVAL_TYPE_MONTH)) {
            xAxisMonthHandler(result);
        } else {
            xAxisDayHandler(result);
        }
        return result;
    }

    private void xAxisYearHandler(Map<String, List<HomeChart>> result) {
        for (Map.Entry<String, List<HomeChart>> entry : result.entrySet()) {
            for (HomeChart homeChart : entry.getValue()) {
                String month = MONTH_STRING_MAP.get(Month.valueOf(homeChart.getXAxis()));
                if (month != null) {
                    homeChart.setXAxis(month);
                }
            }
        }
    }

    private void xAxisMonthHandler(Map<String, List<HomeChart>> result) {
        for (Map.Entry<String, List<HomeChart>> entry : result.entrySet()) {
            for (HomeChart homeChart : entry.getValue()) {
                homeChart.setXAxis(String.format("%s日", homeChart.getXAxis()));
            }
        }
    }

    private void xAxisDayHandler(Map<String, List<HomeChart>> result) {
        for (Map.Entry<String, List<HomeChart>> entry : result.entrySet()) {
            for (HomeChart homeChart : entry.getValue()) {
                homeChart.setXAxis(String.format("%s:00", homeChart.getXAxis()));
            }
        }
    }
}
